package com.bootdo.system.controller;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.bootdo.common.controller.BaseController;
import com.bootdo.common.domain.SysDictDO;
import com.bootdo.common.service.SysDictService;
import com.bootdo.common.utils.R;
import com.bootdo.system.domain.Order;
import com.bootdo.system.domain.Parents;
import com.bootdo.system.domain.RedBag;
import com.bootdo.system.domain.RedBagConfig;
import com.bootdo.system.domain.ShareRecord;
import com.bootdo.system.domain.UserAccount;
import com.bootdo.system.domain.UserAccountDetail;
import com.bootdo.system.domain.VeriCode;
import com.bootdo.system.service.OrderService;
import com.bootdo.system.service.ParentsService;
import com.bootdo.system.service.RedBagConfigService;
import com.bootdo.system.service.RedBagService;
import com.bootdo.system.service.RoleService;
import com.bootdo.system.service.ShareRecordService;
import com.bootdo.system.service.UserAccountDetailService;
import com.bootdo.system.service.UserAccountService;
import com.bootdo.system.service.UserService;
import com.bootdo.system.service.VeriCodeService;
import com.bootdo.system.util.Base64Util;
import com.bootdo.system.util.GetWxOrderno;
import com.bootdo.system.util.RequestHandler;
import com.bootdo.system.util.WeixinUtil;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

@RequestMapping("/sys/wx")
@Controller
@Api(value = "/sys/wx", description = "微信操作接口")
public class WeixinController extends BaseController {
	String prefix = "sys/wx";
	
	@Autowired
	UserService userService;
	@Autowired
	RoleService roleService;
	
	@Autowired
	OrderService orderService;  
	
	@Autowired
	ParentsService parentsService;
	
	@Autowired
	SysDictService sysDictService;
	
	@Autowired
	RedBagService redBagService;
	
	@Autowired
	VeriCodeService veriCodeService;
	
	@Autowired
	RedBagConfigService redBagConfigService;
	
	@Autowired
	ShareRecordService shareRecordService;
	
	@Autowired
	UserAccountService userAccountService;
	
	@Autowired
	UserAccountDetailService userAccountDetailService;
	
	
	@Autowired HttpServletRequest request;
	
	
	@Autowired HttpServletResponse response;
	
    public static final String appid = "wxef0256a5461356df";//在微信开发平台登记的app应用  
    public static final String appsecret = "6dd00f32c68c4a245bad74ecc1dece29";  
    public static final String partner = "1497734702";//商户号  
    public static final String partnerkey ="LiuWa2017Beijing20180101Dannyzhu";//不是商户登录密码，是商户在微信平台设置的32位长度的api秘钥，  
    public static final String createOrderURL="https://api.mch.weixin.qq.com/pay/unifiedorder";  
    public static final String backUri="http://XXXXXXXX/api/weixin/topay.jhtml";//微信支付下单地址  
    public static final String notify_url="http://47.94.23.135:8080/sys/wx/notify";//异步通知地址  
    public static final String SUBMIT_IP="47.94.23.135";//微信支付发起端IP

	@ApiOperation(value="获取用户sessionkey", notes="根据登录获取的code查询session", response=String.class)
	@ApiImplicitParams({
        @ApiImplicitParam(name = "appid", value = "appId", required = true, dataType = "String"),
        @ApiImplicitParam(name = "jsCode", value = "登录时获取的 code", required = true, dataType = "String"),
        @ApiImplicitParam(name = "encryptedData", value = "加密的用户信息", required = true, dataType = "String"),
        @ApiImplicitParam(name = "iv", value = "初始向量", required = true, dataType = "String"),
        @ApiImplicitParam(name = "secret", value = "app的secret", required = true, dataType = "String")
	})
	@GetMapping("/getSessionKey")
	@ResponseBody
	R getSessionKey(String appid, String jsCode, String encryptedData,String iv,String secret) {
			JSONObject sInfo = WeixinUtil.getSessionKey(appid, jsCode, secret);
			JSONObject userInfo = Base64Util.getUserInfo(encryptedData,sInfo.getString("session_key"),iv);
			R r = new R();
			r.put("sInfo",sInfo);
			r.put("userInfo", userInfo);
			System.out.println("sInfo=====" + sInfo);
			System.out.println("userInfo=====" + userInfo);
			//TODO
			Parents p = parentsService.getByOpenId(sInfo.getString("openid"));
			boolean isNew = false;
			if(p == null) {
				p = new Parents();
				p.setCountry(userInfo.getString("country"));
				p.setProvince(userInfo.getString("province"));
				p.setCity(userInfo.getString("city"));
				p.setGender(userInfo.getInteger("gender"));
				p.setNickName(userInfo.getString("nickName"));
				p.setOpenId(sInfo.getString("openid"));
				p.setCreateTime(new Date());
				p.setHeadImg(userInfo.getString("avatarUrl"));
				p.setUnionId(sInfo.getString("unionid"));
				parentsService.save(p);
				isNew = true;
			}else if(p.getPhone() == null || "".equals(p.getPhone())){
				isNew = true;
			}
			r.put("userId", p.getId());
			r.put("isNew", isNew);
			return r;
	}

	
	@ApiOperation(value="领取红包", notes="根据验证码领取红包", response=String.class)
	@ApiImplicitParams({
        @ApiImplicitParam(name = "userId", value = "用户id", required = true,paramType="query", dataType ="Long"),
        @ApiImplicitParam(name = "phone", value = "手机号码", required = true, paramType="query",dataType = "String"),
        @ApiImplicitParam(name = "shareId", value = "分享记录id", required = false, paramType="query",dataType = "Long"),
        @ApiImplicitParam(name = "veriCode", value = "验证码", required = true, paramType="query",dataType = "String")
	})
	@GetMapping("/loginByPhone")
	@ResponseBody
	R loginByPhone(@RequestParam("userId")Long userId ,@RequestParam("phone")String phone,	@RequestParam(value="shareId",required=false)Long shareId,@RequestParam("veriCode")String veriCode) {
			R r = new R();
			VeriCode vc = veriCodeService.getByPhone(phone);
			if(vc == null) {
				return R.error("验证码不存在");
			}
			if(!veriCode.equals(vc.getCode())) {
				return R.error("验证码不正确");
			}
			Map<String,Object> codeMap = new HashMap<String,Object>();
			codeMap.put("type", "codePeriod");
			SysDictDO codeDict =  sysDictService.list(codeMap).get(0);
			if((new Date().getTime()-vc.getCreateTime().getTime()) > Integer.parseInt(codeDict.getValue())*60000) {
				return R.error("验证码已经过期，请重新获取验证码");
			}
			veriCodeService.removeByPhone(phone);
			Parents p = parentsService.get(userId);
			if(p != null) {
				Map<String,Object> dictMap = new HashMap<String,Object>();
				dictMap.put("pageSize", "10");
				dictMap.put("pageNum", "1");
				dictMap.put("type", "liudian");
				List<SysDictDO> dictList = sysDictService.list(dictMap);
				dictMap.put("type", "newBagEndTime");
				SysDictDO newDict =  sysDictService.list(dictMap).get(0);
				dictMap.put("type", "shareBagEndTime");
				SysDictDO shareDict =  sysDictService.list(dictMap).get(0);
				RedBagConfig rbConfig = redBagConfigService.getConfig();
				String userPhone = p.getPhone();
				List<RedBag> list = new ArrayList<RedBag>();
				Date now = new Date();
				Calendar c = Calendar.getInstance();  
				if(userPhone == null || "".equals(userPhone)) {
					int rbCount = rbConfig.getNewCount();
					for(int i=0;i<rbCount;i++) {
						RedBag rb = new RedBag();
						rb.setBagMoney(Double.parseDouble(dictList.get(0).getValue())*rbConfig.getNewPoint());
						rb.setBagName("新用户红包");
						rb.setBagStatus(0);
						rb.setBagType(1);
						rb.setOwnerId(userId);
						rb.setUserPhone(phone);
						 c.setTime(now);  
						 c.add(Calendar.DAY_OF_MONTH, Integer.parseInt(newDict.getValue()));
						rb.setEndTime(c.getTime());
						redBagService.save(rb);
						list.add(rb);
					}
					p.setPhone(phone);
					parentsService.update(p);
				}else if(shareId != null) {
					Map<String,Object> map = new HashMap<String,Object>();
						map.put("ownerId", userId);
						map.put("shareId", shareId);
					int count = redBagService.count(map);
					if(count>0) {
						return R.error("您已抢过该红包!");
					}
					ShareRecord share = shareRecordService.get(shareId);
					if(share == null) {
						return R.error("红包不存在");
					}
					if(share.getRedBagCount()<1) {
						return R.error("红包已抢完");
					}
					RedBag rb = new RedBag();
					int redPoint = 0;
					if(rbConfig.getShareStart() == rbConfig.getShareEnd()) {
						redPoint = rbConfig.getShareStart();
					}else {
						redPoint =  new Random().nextInt(rbConfig.getShareEnd())%(rbConfig.getShareEnd()-rbConfig.getShareStart()+1) + rbConfig.getShareStart();
					}
					rb.setBagMoney(Double.parseDouble(dictList.get(0).getValue())*redPoint);
					rb.setBagName("分享红包");
					rb.setBagStatus(0);
					rb.setOwnerId(userId);
					rb.setBagType(2);
					rb.setShareId(shareId);
					rb.setUserPhone(phone);
					 c.setTime(now);  
					 c.add(Calendar.DAY_OF_MONTH, Integer.parseInt(shareDict.getValue()));
					rb.setEndTime(c.getTime());
					redBagService.save(rb);
					list.add(rb);
					shareRecordService.minusCount(shareId);
				}
				r.put("redBag", list);
				return r;
			}else {
				return R.error("用户不存在");
			}
	}

	
	@ApiOperation(value="跳转到支付", notes="", response=String.class)
	@ApiImplicitParams({
        @ApiImplicitParam(name = "orderNo", value = "订单编号", required = true,paramType="query", dataType = "String"),
        @ApiImplicitParam(name = "mobile", value = "手机号码", required = true,paramType="query", dataType = "String"),
        @ApiImplicitParam(name = "userId", value = "用户id", required = true,paramType="query", dataType = "String"),
        @ApiImplicitParam(name = "redBagId", value = "使用的红包Id", required = false,paramType="query", dataType = "String")
	})
	@GetMapping(value = "/topay")   
	@ResponseBody public R topay(@RequestParam Map<String,Object> param) throws Exception{  
	    request.setCharacterEncoding("UTF-8");  
	    response.setCharacterEncoding("UTF-8");  
	    response.setContentType("text/html;charset=UTF-8");  
	     JSONObject retMsgJson=new JSONObject();  
	     String orderNo = param.get("orderNo")+"";
	     String mobile = param.get("mobile")+"";
	     String userId = param.get("userId")+"";
	    if(orderNo==null||mobile==null){  
	        return R.error("订单或手机号码不存在");  
	    }  
	    Parents member=parentsService.get(Long.parseLong(userId));//获取用户数据  
	    Order order=orderService.getByNo(orderNo);//获取订单数据  
	     //验证订单与支付会员关系  
	     if(member==null||order==null){  
	        return R.error("用户或订单不存在");  
	    }  
	    if(order.getOrderStatus()>1L){  
	        return R.error("订单不是待支付状态");  
	    }  
	    String openId = member.getOpenId().toString();    
	    String money = order.getCoursePrice().toString();//获取订单金额  
	    float moneyF = Float.parseFloat(money);
	    if(param.get("redBagId") != null) {
	    	RedBag redBag = redBagService.get(Long.parseLong(param.get("redBagId")+""));
	    	if(redBag != null) {
	    		RedBagConfig rbc = redBagConfigService.getConfig();
	    		if(rbc == null) {
	    			return R.error(1, "没有红包配置数据，无法查询可用红包");
	    		}
	    		if(redBag.getBagType() == 0) {
	    			moneyF = moneyF - redBag.getBagMoney().floatValue();
	    		}else {
		    		SysDictDO dict = sysDictService.getSingleDictByType("liudian");
		    		if(dict == null) {
		    			return R.error(1, "没有配置遛点与人民币换算关系，无法查询可用红包");
		    		}
		    		moneyF = moneyF - (redBag.getBagMoney().floatValue())*Float.parseFloat(dict.getValue());
	    		}
	    	}
	    }
	    //金额转化为分为单位  
	    float sessionmoney = moneyF;  
	    String finalmoney = String.format("%.2f", sessionmoney);  
	    finalmoney = finalmoney.replace(".", "");  
	            String currTime = new SimpleDateFormat("yyyyMMdd").format(new Date());  
	            //8位日期  
	            String strTime = currTime.substring(8, currTime.length());
	            //四位随机数  
	            String strRandom = WeixinUtil.getRandom(4) + "";  
	            //10位序列号,可以自行调整。  
	            String strReq = strTime + strRandom;          
	            //商户号  
	            String mch_id = partner;  
	            //子商户号  非必输  
	            //String sub_mch_id="";  
	            //设备号   非必输  
	            String device_info="";  
	            //随机数   
	            String nonce_str = strReq;  
	            String body = order.getOrderNo();  
	            //附加数据  
	            String attach = userId;  
	            //商户订单号  
	            String out_trade_no = order.getOrderNo()+"|"+System.currentTimeMillis();//订单编号加时间戳  
	            if(out_trade_no.length()>32) {
	            	out_trade_no = out_trade_no.substring(0, 32);
	            }
	            int intMoney = Integer.parseInt(finalmoney);              
	            //总金额以分为单位，不带小数点  
	            String total_fee = String.valueOf(intMoney);  
	            //订单生成的机器 IP  
	            String spbill_create_ip = SUBMIT_IP;  
	            String notify_url =this.notify_url;//微信异步通知地址           
	            String trade_type = "JSAPI";//app支付必须填写为APP  
	                        //对以下字段进行签名  
	            SortedMap<String, String> packageParams = new TreeMap<String, String>();  
	            packageParams.put("appid", appid);    
	            packageParams.put("attach", attach);   
	            packageParams.put("body", body);    
	            packageParams.put("mch_id", mch_id);      
	            packageParams.put("nonce_str", nonce_str);    
	            packageParams.put("notify_url", notify_url);    
	            packageParams.put("out_trade_no", out_trade_no);      
	            packageParams.put("spbill_create_ip", spbill_create_ip); 
	            packageParams.put("openid", openId);
	            packageParams.put("total_fee", total_fee);  
	            packageParams.put("trade_type", trade_type);    
	            RequestHandler reqHandler = new RequestHandler(request, response);  
	            reqHandler.init(appid, appsecret,partner, partnerkey);        
	            String sign = reqHandler.createSign(packageParams);//获取签名  
	            String xml="<xml>"+  
	                    "<appid>"+appid+"</appid>"+  
	                    "<attach>"+attach+"</attach>"+  
	                    "<body><![CDATA["+body+"]]></body>"+  
	                    "<mch_id>"+mch_id+"</mch_id>"+  
	                    "<nonce_str>"+nonce_str+"</nonce_str>"+  
	                    "<notify_url>"+notify_url+"</notify_url>"+  
	                    "<out_trade_no>"+out_trade_no+"</out_trade_no>"+  
	                    "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+  
	                    "<total_fee>"+total_fee+"</total_fee>"+  
	                    "<trade_type>"+trade_type+"</trade_type>"+  
	                    "<openid>"+openId+"</openid>"+  
	                    "<sign>"+sign+"</sign>"+  
	                    "</xml>";  
	            String allParameters = "";  
	            try {  
	                allParameters =  reqHandler.genPackage(packageParams);  
	             } catch (Exception e) {  
	                // TODO Auto-generated catch block  
	                e.printStackTrace();  
	            }  
	            String createOrderURL = this.createOrderURL;  
	            String prepay_id="";  
	            try {  
	                prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml);  
	                if(prepay_id.equals("")){  
		                return R.error("prepay_id为空");  
	                 }  
	            } catch (Exception e1) {  
	                e1.printStackTrace();  
	             }  
	            //获取到prepayid后对以下字段进行签名最终发送给app  
	            SortedMap<String, String> finalpackage = new TreeMap<String, String>(); 
	            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH:mm:ss"); 
	            String timestamp = sdf.format(System.currentTimeMillis());  
	            finalpackage.put("appId", appid);    
	            finalpackage.put("timeStamp", timestamp);    
	            finalpackage.put("nonceStr", nonce_str);    
	            finalpackage.put("package", "prepay_id="+prepay_id);                
	            finalpackage.put("signType", "MD5");  
	            finalpackage.put("key", partnerkey); 
	            String finalsign = reqHandler.createSign(finalpackage);  
	                    retMsgJson.put("msg", "ok");  
	                    retMsgJson.put("appid", appid);  
	                    retMsgJson.put("timestamp", timestamp);  
	                    retMsgJson.put("noncestr", nonce_str);  
	                    retMsgJson.put("partnerid", partner);  
	                    retMsgJson.put("prepayid", prepay_id);  
	                    retMsgJson.put("package",  "prepay_id="+prepay_id);  
	                    retMsgJson.put("signType", "MD5");  
	                    retMsgJson.put("openid", openId); 
	                    retMsgJson.put("sign", finalsign);  
	                    return R.ok(retMsgJson);
	}  
	
	//微信异步通知  
	@PostMapping(value = "/notify")   
	@ResponseBody public void notify(HttpServletRequest request,HttpServletResponse response) throws Exception{  
	    request.setCharacterEncoding("UTF-8");  
	    response.setCharacterEncoding("UTF-8");  
	    response.setContentType("text/html;charset=UTF-8");  
	    response.setHeader("Access-Control-Allow-Origin", "*");   
	    InputStream in=request.getInputStream();  
	    ByteArrayOutputStream out=new ByteArrayOutputStream();  
	    byte[] buffer =new byte[1024];  
	    int len=0;  
	    while((len=in.read(buffer))!=-1){  
	        out.write(buffer, 0, len);  
	    }  
	    out.close();  
	    in.close();  
	    String msgxml=new String(out.toByteArray(),"utf-8");//xml数据  
	    System.out.println(msgxml);
	    Map map =  new GetWxOrderno().doXMLParse(msgxml);  
	    String result_code=(String) map.get("result_code");  
	    String out_trade_no  = (String) map.get("out_trade_no");  
	    String total_fee  = (String) map.get("total_fee");  
	    String sign  = (String) map.get("sign");  
	    Double amount=new Double(total_fee)/100;//获取订单金额  
	    String attach= (String) map.get("attach");  
	    String sn=out_trade_no.split("\\|")[0];//获取订单编号  
	    Long memberid=Long.valueOf(attach);  
	    Parents member=parentsService.get(memberid);  
	    Order order=orderService.getByNo(sn);  
	    if(result_code.equals("SUCCESS")&&member!=null&&order!=null){  
	      //验证签名  
	        float sessionmoney = Float.parseFloat(order.getCoursePrice().toString());  
	        String finalmoney = String.format("%.2f", sessionmoney);  
	        finalmoney = finalmoney.replace(".", "");  
	        int intMoney = Integer.parseInt(finalmoney);              
	        //总金额以分为单位，不带小数点  
	        String order_total_fee = String.valueOf(intMoney);  
	        String fee_type  = (String) map.get("fee_type");  
	        String bank_type  = (String) map.get("bank_type");  
	        String cash_fee  = (String) map.get("cash_fee");  
	        String is_subscribe  = (String) map.get("is_subscribe");  
	        String nonce_str  = (String) map.get("nonce_str");  
	        String openid  = (String) map.get("openid");  
	        String return_code  = (String) map.get("return_code");  
	        String sub_mch_id  = (String) map.get("sub_mch_id");  
	        String time_end  = (String) map.get("time_end");  
	        String trade_type  = (String) map.get("trade_type");  
	        String transaction_id  = (String) map.get("transaction_id");  
	                //需要对以下字段进行签名  
	        SortedMap<String, String> packageParams = new TreeMap<String, String>();  
	        packageParams.put("appid", appid);    
	        packageParams.put("attach", memberid.toString()); //用自己系统的数据：会员id  
	        packageParams.put("bank_type", bank_type);    
	        packageParams.put("cash_fee", cash_fee);    
	        packageParams.put("fee_type", fee_type);      
	        packageParams.put("is_subscribe", is_subscribe);    
	        packageParams.put("mch_id", partner);    
	        packageParams.put("nonce_str", nonce_str);        
	        packageParams.put("openid", openid);   
	        packageParams.put("out_trade_no", out_trade_no);  
	        packageParams.put("result_code", result_code);    
	        packageParams.put("return_code", return_code);        
	        packageParams.put("sub_mch_id", sub_mch_id);   
	        packageParams.put("time_end", time_end);  
	        packageParams.put("total_fee", order_total_fee);    //用自己系统的数据：订单金额  
	        packageParams.put("trade_type", trade_type);   
	        packageParams.put("transaction_id", transaction_id);  
	        RequestHandler reqHandler = new RequestHandler(request, response);  
	        reqHandler.init(appid, appsecret,partner, partnerkey);        
	        String endsign = reqHandler.createSign(packageParams);  
	        if(sign.equals(endsign)){//验证签名是否正确  官方签名工具地址：https://pay.weixin.qq.com/wiki/tools/signverify/   
	           if(order.getOrderStatus()==0){  
	            	if(order.getRedBagId() != null) {
	            		//如果使用了红包，则支付成功后，将该红包从表里删除
	            		redBagService.remove(order.getRedBagId());
	            	}
	            	if(order.getAccountTotal() != null && order.getAccountTotal()>0) {
	            		//如果使用了账户中的遛点，则支付成功后，从账户中扣除相应的遛点
	            		UserAccount ua = userAccountService.get(order.getParentsId());
	            		ua.setPoint(ua.getPoint() - order.getAccountTotal());
	            		userAccountService.update(ua);
	            		UserAccountDetail uad = new UserAccountDetail();
	            		uad.setDealTime(new Date());
	            		uad.setDealType("支出");
	            		uad.setPoint(order.getAccountTotal());
	            		uad.setUserId(order.getParentsId());
	            		userAccountDetailService.save(uad);
	            	}
	            	order.setOrderStatus(1L);
	            	orderService.update(order);
	                response.getWriter().write(setXml("SUCCESS", "OK"));    //告诉微信已经收到通知了  
	            }else {
	            	 response.getWriter().write(setXml("FAILED", "订单不是待支付状态"));    //告诉微信已经收到通知了  
	            }
	      
	        }  
	    }  
	   }  
	
	public static String setXml(String return_code,String return_msg){  
	        return "<xml><return_code><![CDATA["+return_code+"]]></return_code><return_msg><![CDATA["+return_msg+"]]></return_msg></xml>";  
	}  
	
}
